package com.skyworth.util.imageloader.fresco;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.facebook.cache.common.CacheKey;
import com.facebook.cache.common.SimpleCacheKey;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.datasource.DataSubscriber;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.controller.ControllerListener;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.BasePostprocessor;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.skyworth.util.ThreadPool;
import com.skyworth.util.Util;
import com.skyworth.util.imageloader.CircleParams;
import com.skyworth.util.imageloader.FinalCallback;
import com.skyworth.util.imageloader.IImageLoader;
import com.skyworth.util.imageloader.LoaderData;
import com.skyworth.util.imageloader.OnBitmapLoadListener;
import com.skyworth.util.imageloader.Transform;

import static com.skyworth.util.imageloader.fresco.CoocaaFresco.isInit;

/**
 * Created by luwei on 15-12-28.
 */
public class FrescoImgLoader implements IImageLoader {
    private LoaderData loaderData = null;
    private Object lock = new Object();
    private Handler mainThread = new Handler(Looper.getMainLooper());

    @Override
    public IImageLoader setScaleType(ImageView.ScaleType scaleType) {
        loaderData.setScaleType(scaleType);
        return this;
    }

    @Override
    public IImageLoader wrapContent() {
        loaderData.setWrapContent(true);
        return this;
    }

    @Override
    public void updateThreadPriority(int priopity) {
        try {
            CoocaaFresco.updateThreadPriority(priopity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public IImageLoader with(Context context) {
        synchronized (lock) {
            while (loaderData != null) {
                try {
                    lock.wait(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        loaderData = new LoaderData();
        loaderData.setContext(context);
        return this;
    }

    @Override
    public void init(Context context) {
        CoocaaFresco.initialize(context);
    }


    @Override
    public void destroy() {
        notifyThread();
        CoocaaFresco.destroy();
//        Fresco.shutDown();
    }

    @Override
    public View getView(Context context) {
        if (!isInit()) {
            return new ImageView(context);
        }
        return new CoocaaDraweeView(context);
    }

    @Override
    public IImageLoader setPlaceHolder(int id) {
        loaderData.setPlaceResource(id);
        return this;
    }

    @Override
    public IImageLoader setPlaceHolder(Drawable drawable) {
        loaderData.setPlaceDrawable(drawable);
        return this;
    }

    @Override
    public IImageLoader transform(Transform transform) {
        loaderData.setTransform(transform);
        return this;
    }

    @Override
    public IImageLoader load(Uri uri) {
        loaderData.setUri(uri);
        return this;
    }

    @Override
    public IImageLoader load(int id) {
        loaderData.setImageId(id);
        return this;
    }

    @Override
    public IImageLoader small(Uri uri) {
        loaderData.setSmall(uri);
        return this;
    }

    @Override
    public IImageLoader resize(int width, int height) {
        loaderData.setWidth(width);
        loaderData.setHeight(height);
        return this;
    }

    @Override
    public IImageLoader finalCallback(FinalCallback callback) {
        loaderData.setCallback(callback);
        return this;
    }

    @Override
    public IImageLoader setLeftTopCorner(float corner) {
        loaderData.setLeftTopCorner(corner);
        return this;
    }

    @Override
    public IImageLoader setLeftBottomCorner(float corner) {
        loaderData.setLeftBottomCorner(corner);
        return this;
    }

    @Override
    public IImageLoader setRightTopCorner(float corner) {
        loaderData.setRightTopCorner(corner);
        return this;
    }

    @Override
    public IImageLoader setRightBottomCorner(float corner) {
        loaderData.setRightBottomCorner(corner);
        return this;
    }

    @Override
    public void clearCacheFromMemory(String url) {
        notifyThread();
        if (!isInit()) {
            return;
        }
        ImagePipeline imagePipeline = CoocaaFresco.getImagePipeline();
        if (imagePipeline != null)
            imagePipeline.evictFromMemoryCache(Uri.parse(url));
    }

    @Override
    public IImageLoader showAnimation(boolean show) {
        loaderData.setShowAnimation(show);
        return this;
    }

    @Override
    public IImageLoader circle(CircleParams params) {
        loaderData.setCircleParams(params);
        return this;
    }

    @Override
    public void clearCache(String url) {
        notifyThread();
        if (!isInit()) {
            return;
        }
        ImagePipeline imagePipeline = CoocaaFresco.getImagePipeline();
        if (imagePipeline != null) {
            imagePipeline.evictFromCache(Uri.parse(url));
        }
    }

    @Override
    public void into(final View view) {
        if (!isInit()) {
            notifyThread();
            return;
        }
        final LoaderData data = new LoaderData(loaderData);
        if (view instanceof CoocaaDraweeView) {
            final CoocaaDraweeView draweeView = (CoocaaDraweeView) view;
            if (!data.isShowAnimation())
                draweeView.getHierarchy().setFadeDuration(0);
            draweeView.getHierarchy().reset();
            if (data.getScaleType() != null)
                draweeView.getHierarchy().setActualImageScaleType(changeScaleType(data.getScaleType()));
            if (data.getPlaceResource() != 0)
                draweeView.getHierarchy().setPlaceholderImage(data.getPlaceResource());
            if (data.getPlaceDrawable() != null)
                draweeView.getHierarchy().setPlaceholderImage(data.getPlaceDrawable());
            ImageRequestBuilder requestBuilder = null;
            if (data.getImageId() != 0) {
                data.setUri(Uri.parse(CoocaaFresco.getFrescoResUri(data.getContext(), data.getImageId())));
            }
            if (data.getWidth() != 0 && data.getHeight() != 0) {
                requestBuilder = ImageRequestBuilder.newBuilderWithSource(data.getUri())
                        .setResizeOptions(new ResizeOptions(data.getWidth(), data.getHeight()));
            } else {
                requestBuilder = ImageRequestBuilder.newBuilderWithSource(data.getUri());
            }
            if (data.getTransform() != null) {
                if (!data.getTransform().isNewBitmap()) {
                    requestBuilder.setPostprocessor(new BasePostprocessor() {
                        @Override
                        public void process(Bitmap bitmap) {
                            data.getTransform().transform(bitmap);
                        }

                        @Override
                        public CacheKey getPostprocessorCacheKey() {
                            return new SimpleCacheKey(data.getUri().toString());
                        }
                    });
                } else {
                    requestBuilder.setPostprocessor(new BasePostprocessor() {
                        @Override
                        public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
                            if (data.getWidth() != 0 && data.getHeight() != 0) {
                                CloseableReference<Bitmap> bitmapRef = null;
                                try {
                                    bitmapRef = bitmapFactory.createBitmap(
                                            data.getWidth(), data.getHeight(), sourceBitmap.getConfig());
                                    data.getTransform().transform(bitmapRef.get(), sourceBitmap);
                                    return CloseableReference.cloneOrNull(bitmapRef);
                                } finally {
                                    CloseableReference.closeSafely(bitmapRef);
                                }

                            } else {
                                return super.process(sourceBitmap, bitmapFactory);
                            }
                        }

                        @Override
                        public CacheKey getPostprocessorCacheKey() {
                            return new SimpleCacheKey(data.getUri().toString());
                        }
                    });

                }
            }

            final PipelineDraweeControllerBuilder builder = CoocaaFresco.newDraweeControllerBuilder(data.getContext())
                    .setOldController(draweeView.getController());
            if (data.getSmall() != null)
                builder.setLowResImageRequest(ImageRequest.fromUri(data.getSmall()));
            builder.setImageRequest(requestBuilder.build());
            if (data.isWrapContent() || data.getCallback() != null) {
                ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
                    @Override
                    public void onFinalImageSet(String id, final ImageInfo imageInfo, Animatable animatable) {
                        super.onFinalImageSet(id, imageInfo, animatable);
                        if (data.isWrapContent()) {
                            view.post(new Runnable() {
                                @Override
                                public void run() {
                                    ViewGroup.LayoutParams params = view.getLayoutParams();
                                    if (params == null) {
                                        params = new ViewGroup.LayoutParams(Util.Div(imageInfo.getWidth()), Util.Div(imageInfo.getHeight()));
                                    } else {
                                        params.height = Util.Div(imageInfo.getHeight());
                                        params.width = Util.Div(imageInfo.getWidth());
                                    }
                                    view.setLayoutParams(params);
                                }
                            });
                        }
                        if (data.getCallback() != null)
                            data.getCallback().onFinal(id, imageInfo.getWidth(), imageInfo.getHeight());

                    }

                    @Override
                    public void onFailure(String id, Throwable throwable) {
                        super.onFailure(id, throwable);
                        if (data.getCallback() != null)
                            data.getCallback().onFailed(id, throwable);
                    }
                };
                builder.setControllerListener(controllerListener);
            }
            if (data.getLeftTopCorner() != 0 || data.getLeftBottomCorner() != 0
                    || data.getRightTopCorner() != 0 || data.getRightBottomCorner() != 0) {
                RoundingParams roundingParams = RoundingParams.fromCornersRadii(
                        data.getLeftTopCorner(), data.getRightTopCorner(),
                        data.getRightBottomCorner(), data.getLeftBottomCorner());
                draweeView.getHierarchy().setRoundingParams(roundingParams);
            }

            if (data.getCircleParams() != null) {
                RoundingParams params = RoundingParams.fromCornersRadius(data.getCircleParams().getRadius());
                params.setRoundAsCircle(true);
                draweeView.getHierarchy().setRoundingParams(params);
            }
            if (Looper.myLooper() == Looper.getMainLooper())
                draweeView.setController(builder.build());
            else
                mainThread.post(new Runnable() {
                    @Override
                    public void run() {
                        draweeView.setController(builder.build());
                    }
                });
        } else {
            notifyThread();
            throw new RuntimeException("the view must be CoocaaDraweeView in fresco");
        }
        notifyThread();
    }

    @Override
    public void getBitmap(final OnBitmapLoadListener listener) {
        if (!isInit()) {
            notifyThread();
            return;
        }
        try {
            final LoaderData data = new LoaderData(loaderData);
            ImagePipeline imagePipeline = CoocaaFresco.getImagePipeline();
            ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(data.getUri());
            if (data.getWidth() > 0 && data.getHeight() > 0) {
                builder.setResizeOptions(new ResizeOptions(data.getWidth(), data.getHeight()));
            }
            ImageRequest request = builder.build();
            DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(request, data.getContext());
            if (listener != null) {
                DataSubscriber dataSubscriber = new BaseBitmapDataSubscriber() {
                    @Override
                    public void onNewResultImpl(@Nullable Bitmap bitmap) {
                        listener.loadSuccess(bitmap, data.getUri().toString());
                    }

                    @Override
                    public void onFailureImpl(DataSource dataSource) {
                        listener.loadFailed(data.getUri().toString());
                    }
                };
                dataSource.subscribe(dataSubscriber, ThreadPool.getInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        notifyThread();
    }

    @Override
    public void startAnimation(View view) {
        notifyThread();
        if (!isInit()) {
            return;
        }
        if (view instanceof CoocaaDraweeView)
            if (((CoocaaDraweeView) view).getController() != null && ((CoocaaDraweeView) view).getController().getAnimatable() != null)
                ((CoocaaDraweeView) view).getController().getAnimatable().start();
    }

    @Override
    public void reset(View view) {
        notifyThread();
        if (!isInit()) {
            return;
        }
        if (view instanceof CoocaaDraweeView) {
            ((CoocaaDraweeView) view).setController(null);
        } else {
            throw new RuntimeException("the view must be CoocaaDraweeView in fresco");
        }
    }

    @Override
    public void stopAnimation(View view) {
        notifyThread();
        if (!isInit()) {
            return;
        }
        if (view instanceof CoocaaDraweeView)
            if (((CoocaaDraweeView) view).getController() != null && ((CoocaaDraweeView) view).getController().getAnimatable() != null)
                ((CoocaaDraweeView) view).getController().getAnimatable().stop();
    }

    private void notifyThread() {
        if (loaderData != null) {
            loaderData = null;
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }

    public ScalingUtils.ScaleType changeScaleType(ImageView.ScaleType scaleType) {
        switch (scaleType) {
            case FIT_XY:
                return ScalingUtils.ScaleType.FIT_XY;
            case FIT_CENTER:
                return ScalingUtils.ScaleType.FIT_CENTER;
            case CENTER:
                return ScalingUtils.ScaleType.CENTER;
            case CENTER_CROP:
                return ScalingUtils.ScaleType.CENTER_CROP;
            case CENTER_INSIDE:
                return ScalingUtils.ScaleType.CENTER_INSIDE;
            case FIT_START:
                return ScalingUtils.ScaleType.FIT_START;
        }
        return ScalingUtils.ScaleType.CENTER;
    }
}
